/**
 * @project: parallel-task
 * @package: com.ngplat.paralleltask.spring
 * @filename: TaskBeanContainer.java
 *
 * Copyright (c) 2018 eSunny Info. Tech Ltd. All rights reserved.
 * 
 */
package com.ngplat.paralleltask.spring;

import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.PriorityOrdered;
import org.springframework.stereotype.Component;

import com.ngplat.paralleltask.annotation.TaskBean;
import com.ngplat.paralleltask.model.TaskInfo;
import com.ngplat.paralleltask.task.TaskPoolManager;
import com.ngplat.paralleltask.task.TaskProcessor;

/**
 * @typename: TaskBeanContainer
 * @brief: TaskBean Container
 * @author: KI ZCQ
 * @date: 2018年5月14日 上午11:14:45
 * @version: 1.0.0
 * @since
 * 
 */
@Component
public class TaskBeanContainer implements ApplicationContextAware, PriorityOrdered {

	private static final Log LOG = LogFactory.getLog(TaskBeanContainer.class);

	// Spring应用上下文环境
	private ApplicationContext applicationContext;
	private AtomicBoolean initing = new AtomicBoolean(false);
	private CountDownLatch hasInit = new CountDownLatch(1);
	private volatile String springContainerInstanceFlag = "";
	
	/**
	 * @brief 设置spring构建优先级
	 * @see org.springframework.core.Ordered#getOrder()
	 */
	@Override
	public int getOrder() {
		return PriorityOrdered.HIGHEST_PRECEDENCE;
	}

	/**
	 * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
	 */
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
		String newValue = String.valueOf(applicationContext.hashCode());
		LOG.info("fetcherBean container id:" + newValue);
		// 不同的Spring Context Refreshing, 允许重新初始化，此处不会有并发
		if (!springContainerInstanceFlag.equals(newValue)) {
			hasInit = new CountDownLatch(1);
			initing.set(false);
			initFetcherContainer();
			springContainerInstanceFlag = newValue;
		}
	}

	/**
	 * 
	 * @Description: 开始注册Bean
	 */
	public void initFetcherContainer() {
		initFetcherContainer(applicationContext);
	}

	/**
	 * @Description: 初始化Fetcher容器  
	 * @param factory
	 */
	public void initFetcherContainer(ListableBeanFactory factory) {
		if (initing.get()) {
			waitInit();
			return;
		}
		if (initing.compareAndSet(false, true)) {
			Map<String, Object> fetcherServices = factory.getBeansWithAnnotation(TaskBean.class);
			
			for(String beanName : fetcherServices.keySet()) {
				Object bean = fetcherServices.get(beanName);
				regiserOneTask(beanName, bean);
			}
			
			hasInit.countDown();
		} else {
			waitInit();
		}
	}
	
	/**
	 * @Description: 向容器中注册一个task 
	 * @param service
	 */
	public static void regiserOneTask(String beanName, Object service) {
    	Class<?> clazz = service.getClass();
    	// 必须是WorkerTask的子类
    	if( (service instanceof TaskProcessor) && (clazz.getAnnotation(TaskBean.class) != null) ) {
    		// 得到注解
    		TaskBean bean = (TaskBean) clazz.getAnnotation(TaskBean.class);
    		// bean不可为null且Id不允许为空
    		if (bean != null && StringUtils.isNotBlank(bean.taskId())) {
    			String taskId = bean.taskId();
        		
        		// 构建TaskInfo
        		TaskInfo task = new TaskInfo(taskId, bean.name());
        		task.setPriority(bean.priority());
        		task.setStatus(bean.status());
        		task.setParentIds(bean.parentIds());
        		task.setJobId(bean.jobId());
        		
        		// 添加任务到任务池中
        		TaskPoolManager.DEFAULT.addTask(task, (TaskProcessor)service);
        		
    		} else {
    			// new exception
    			throw new RuntimeException("Build TaskInfo Error. Bean is null or TaskId is null.");
    		}
    	}
    }
	
    /**
     * @Description: wait for init
     */
	private void waitInit() {
        try {
            hasInit.await();
        } catch (InterruptedException e) {
            LOG.error("Interrupted while waiting init TaskBeanContainer.", e);
        }
    }
	
}
